Data Preparation

library(tidyverse)
## ── Attaching core tidyverse packages ──────────────────────── tidyverse 2.0.0 ──
## ✔ dplyr     1.1.4     ✔ readr     2.1.5
## ✔ forcats   1.0.0     ✔ stringr   1.5.1
## ✔ ggplot2   3.5.1     ✔ tibble    3.2.1
## ✔ lubridate 1.9.4     ✔ tidyr     1.3.1
## ✔ purrr     1.0.4     
## ── Conflicts ────────────────────────────────────────── tidyverse_conflicts() ──
## ✖ dplyr::filter() masks stats::filter()
## ✖ dplyr::lag()    masks stats::lag()
## ℹ Use the conflicted package (<http://conflicted.r-lib.org/>) to force all conflicts to become errors
library(dplyr)
library(car)
## Loading required package: carData
## 
## Attaching package: 'car'
## 
## The following object is masked from 'package:dplyr':
## 
##     recode
## 
## The following object is masked from 'package:purrr':
## 
##     some
library(ggplot2)
library(lubridate)
data <- read_csv("study_tasks.csv")
## Rows: 49244 Columns: 35
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr   (9): TaskID, ActionID, distance, direction, complexity, zoomDirection,...
## dbl  (22): UserID, main_translation_x, main_translation_y, main_translation_...
## lgl   (3): rotateGlobeWhileDragging, oneHandedRotationGesture, moveGlobeWhil...
## dttm  (1): Date
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
demographic <- read_csv("final_introductory.csv")
## Rows: 12 Columns: 8
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (7): Timestamp, Academic_level, Gender, Age_group, Exp_ARVR, Globe_usage...
## dbl (1): UserID
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
positioning_NRG <- read_csv("final_positioning_NRG.csv")
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
positioning_RG <- read_csv("final_positioning_RG.csv")
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
positioning_preference <- read_csv("final_positioning_comparison.csv")
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Positioning_preference, Positioning_feedback
## dbl (1): UserID
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
rotation_OH <- read_csv("final_rotation_OH.csv")
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
rotation_TH <- read_csv("final_rotation_TH.csv")
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
rotation_preference <- read_csv("final_rotation_comparison.csv")
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Rotation_preference, Rotation_feedback
## dbl (1): UserID
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
scale_MG <- read_csv("final_scale_MG.csv")
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
scale_NMG <- read_csv("final_scale_NMG.csv")
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Mentally_demanding, Physically_demanding
## dbl (1): UserID
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
scale_preference <- read_csv("final_scale_comparison.csv")
## Rows: 12 Columns: 4
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (3): Timestamp, Scale_preference, Scale_feedback
## dbl (1): UserID
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
combined_preference <- read_csv("final_outro_comparison.csv")
## Rows: 12 Columns: 6
## ── Column specification ────────────────────────────────────────────────────────
## Delimiter: ","
## chr (5): Timestamp, Combined_positioning_preference, Combined_rotation_prefe...
## dbl (1): UserID
## 
## ℹ Use `spec()` to retrieve the full column specification for this data.
## ℹ Specify the column types or set `show_col_types = FALSE` to quiet this message.
str(data)
## spc_tbl_ [49,244 × 35] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ UserID                  : num [1:49244] 1 1 1 1 1 1 1 1 1 1 ...
##  $ TaskID                  : chr [1:49244] "U1_P_RGFH_0001" "U1_P_RGFH_0001" "U1_P_RGFH_0001" "U1_P_RGFH_0001" ...
##  $ ActionID                : chr [1:49244] "F3432F60-93B4-41D3-A42A-81FC466E2A87" "F3432F60-93B4-41D3-A42A-81FC466E2A87" "F3432F60-93B4-41D3-A42A-81FC466E2A87" "F3432F60-93B4-41D3-A42A-81FC466E2A87" ...
##  $ rotateGlobeWhileDragging: logi [1:49244] TRUE TRUE TRUE TRUE TRUE TRUE ...
##  $ oneHandedRotationGesture: logi [1:49244] TRUE TRUE TRUE TRUE TRUE TRUE ...
##  $ moveGlobeWhileScaling   : logi [1:49244] FALSE FALSE FALSE FALSE FALSE FALSE ...
##  $ distance                : chr [1:49244] "far" "far" "far" "far" ...
##  $ direction               : chr [1:49244] "horizontal" "horizontal" "horizontal" "horizontal" ...
##  $ complexity              : chr [1:49244] "simple" "simple" "simple" "simple" ...
##  $ zoomDirection           : chr [1:49244] "smallToLarge" "smallToLarge" "smallToLarge" "smallToLarge" ...
##  $ Date                    : POSIXct[1:49244], format: "2025-04-23 05:27:13" "2025-04-23 05:27:13" ...
##  $ Type                    : chr [1:49244] "positionTask" "positionTask" "positionTask" "positionTask" ...
##  $ ActionStatus            : chr [1:49244] "dragStart" "drag" "drag" "drag" ...
##  $ main_translation_x      : num [1:49244] -1.201 -1.201 -1.178 -0.949 -0.623 ...
##  $ main_translation_y      : num [1:49244] 1.86 1.86 1.86 1.85 1.78 ...
##  $ main_translation_z      : num [1:49244] -2.35 -2.35 -2.35 -2.43 -2.48 ...
##  $ main_rotation_x         : num [1:49244] 0 0 0 0 0 0 0 0 0 0 ...
##  $ main_rotation_y         : num [1:49244] 1 1 1 0.999 0.993 ...
##  $ main_rotation_z         : num [1:49244] 0 0 0 0 0 0 0 0 0 0 ...
##  $ main_rotation_w         : num [1:49244] 7.55e-08 8.24e-08 4.61e-03 5.19e-02 1.16e-01 ...
##  $ main_scale_x            : num [1:49244] 1 1 1 1 1 ...
##  $ main_scale_y            : num [1:49244] 1 1 1 1 1 ...
##  $ main_scale_z            : num [1:49244] 1 1 1 1 1 ...
##  $ target_translation_x    : num [1:49244] 1.2 1.2 1.2 1.2 1.2 1.2 1.2 1.2 1.2 1.2 ...
##  $ target_translation_y    : num [1:49244] 0.975 0.975 0.975 0.975 0.975 ...
##  $ target_translation_z    : num [1:49244] -2.25 -2.25 -2.25 -2.25 -2.25 -2.25 -2.25 -2.25 -2.25 -2.25 ...
##  $ target_rotation_x       : num [1:49244] 0 0 0 0 0 0 0 0 0 0 ...
##  $ target_rotation_y       : num [1:49244] 1 1 1 1 1 1 1 1 1 1 ...
##  $ target_rotation_z       : num [1:49244] 0 0 0 0 0 0 0 0 0 0 ...
##  $ target_rotation_w       : num [1:49244] 7.55e-08 7.55e-08 7.55e-08 7.55e-08 7.55e-08 ...
##  $ target_scale_x          : num [1:49244] 1 1 1 1 1 1 1 1 1 1 ...
##  $ target_scale_y          : num [1:49244] 1 1 1 1 1 1 1 1 1 1 ...
##  $ target_scale_z          : num [1:49244] 1 1 1 1 1 1 1 1 1 1 ...
##  $ match_accuracy_result   : num [1:49244] 0 0 0 0 0 0 0 0 0 0 ...
##  $ status                  : chr [1:49244] "Attempt started" "Attempting" "Attempting" "Attempting" ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   UserID = col_double(),
##   ..   TaskID = col_character(),
##   ..   ActionID = col_character(),
##   ..   rotateGlobeWhileDragging = col_logical(),
##   ..   oneHandedRotationGesture = col_logical(),
##   ..   moveGlobeWhileScaling = col_logical(),
##   ..   distance = col_character(),
##   ..   direction = col_character(),
##   ..   complexity = col_character(),
##   ..   zoomDirection = col_character(),
##   ..   Date = col_datetime(format = ""),
##   ..   Type = col_character(),
##   ..   ActionStatus = col_character(),
##   ..   main_translation_x = col_double(),
##   ..   main_translation_y = col_double(),
##   ..   main_translation_z = col_double(),
##   ..   main_rotation_x = col_double(),
##   ..   main_rotation_y = col_double(),
##   ..   main_rotation_z = col_double(),
##   ..   main_rotation_w = col_double(),
##   ..   main_scale_x = col_double(),
##   ..   main_scale_y = col_double(),
##   ..   main_scale_z = col_double(),
##   ..   target_translation_x = col_double(),
##   ..   target_translation_y = col_double(),
##   ..   target_translation_z = col_double(),
##   ..   target_rotation_x = col_double(),
##   ..   target_rotation_y = col_double(),
##   ..   target_rotation_z = col_double(),
##   ..   target_rotation_w = col_double(),
##   ..   target_scale_x = col_double(),
##   ..   target_scale_y = col_double(),
##   ..   target_scale_z = col_double(),
##   ..   match_accuracy_result = col_double(),
##   ..   status = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>
summary(data)
##      UserID          TaskID            ActionID        
##  Min.   : 1.000   Length:49244       Length:49244      
##  1st Qu.: 4.000   Class :character   Class :character  
##  Median : 7.000   Mode  :character   Mode  :character  
##  Mean   : 6.741                                        
##  3rd Qu.:10.000                                        
##  Max.   :12.000                                        
##  rotateGlobeWhileDragging oneHandedRotationGesture moveGlobeWhileScaling
##  Mode :logical            Mode :logical            Mode :logical        
##  FALSE:36803              FALSE:11933              FALSE:46552          
##  TRUE :12441              TRUE :37311              TRUE :2692           
##                                                                         
##                                                                         
##                                                                         
##    distance          direction          complexity        zoomDirection     
##  Length:49244       Length:49244       Length:49244       Length:49244      
##  Class :character   Class :character   Class :character   Class :character  
##  Mode  :character   Mode  :character   Mode  :character   Mode  :character  
##                                                                             
##                                                                             
##                                                                             
##       Date                            Type           ActionStatus      
##  Min.   :2025-04-23 05:27:13.00   Length:49244       Length:49244      
##  1st Qu.:2025-04-25 01:36:58.00   Class :character   Class :character  
##  Median :2025-04-26 00:45:01.00   Mode  :character   Mode  :character  
##  Mean   :2025-04-27 21:46:53.98                                        
##  3rd Qu.:2025-05-01 07:26:51.00                                        
##  Max.   :2025-05-05 23:37:33.00                                        
##  main_translation_x  main_translation_y main_translation_z main_rotation_x   
##  Min.   :-7.099065   Min.   :-0.3298    Min.   :-3.487     Min.   :-0.97540  
##  1st Qu.:-0.400000   1st Qu.: 0.9000    1st Qu.:-1.921     1st Qu.:-0.03161  
##  Median :-0.004060   Median : 0.9000    Median :-1.500     Median : 0.00000  
##  Mean   :-0.005048   Mean   : 1.2326    Mean   :-1.683     Mean   :-0.03896  
##  3rd Qu.: 0.400000   3rd Qu.: 1.5539    3rd Qu.:-1.500     3rd Qu.: 0.00000  
##  Max.   : 3.256168   Max.   : 3.8304    Max.   : 5.006     Max.   : 0.97834  
##  main_rotation_y   main_rotation_z    main_rotation_w       main_scale_x    
##  Min.   :-1.0000   Min.   :-0.97710   Min.   :-0.9997261   Min.   :0.08431  
##  1st Qu.:-0.2033   1st Qu.: 0.00000   1st Qu.: 0.0000001   1st Qu.:0.99989  
##  Median : 0.9601   Median : 0.00000   Median : 0.0626987   Median :1.00000  
##  Mean   : 0.5003   Mean   : 0.01287   Mean   : 0.2756917   Mean   :0.99575  
##  3rd Qu.: 1.0000   3rd Qu.: 0.00000   3rd Qu.: 0.6346812   3rd Qu.:1.00002  
##  Max.   : 1.0000   Max.   : 0.98922   Max.   : 0.9999814   Max.   :7.69231  
##   main_scale_y      main_scale_z     target_translation_x target_translation_y
##  Min.   :0.08431   Min.   :0.08431   Min.   :-3.10000     Min.   :0.613       
##  1st Qu.:0.99994   1st Qu.:0.99990   1st Qu.:-0.40000     1st Qu.:0.900       
##  Median :1.00000   Median :1.00000   Median : 0.00000     Median :0.900       
##  Mean   :0.99577   Mean   :0.99576   Mean   :-0.02449     Mean   :1.245       
##  3rd Qu.:1.00002   3rd Qu.:1.00002   3rd Qu.: 0.40000     3rd Qu.:1.773       
##  Max.   :7.69231   Max.   :7.69231   Max.   : 2.33777     Max.   :2.547       
##  target_translation_z target_rotation_x target_rotation_y target_rotation_z 
##  Min.   :-3.3210      Min.   :-0.3928   Min.   :-0.6935   Min.   :-0.21194  
##  1st Qu.:-1.9598      1st Qu.:-0.3584   1st Qu.:-0.5655   1st Qu.: 0.00000  
##  Median :-1.5000      Median : 0.0000   Median : 1.0000   Median : 0.00000  
##  Mean   :-1.6971      Mean   :-0.1153   Mean   : 0.3768   Mean   :-0.01644  
##  3rd Qu.:-1.5000      3rd Qu.: 0.0000   3rd Qu.: 1.0000   3rd Qu.: 0.00000  
##  Max.   :-0.8953      Max.   : 0.0000   Max.   : 1.0000   Max.   : 0.13795  
##  target_rotation_w    target_scale_x   target_scale_y   target_scale_z  
##  Min.   :-0.9761015   Min.   :0.1700   Min.   :0.1700   Min.   :0.1700  
##  1st Qu.: 0.0000001   1st Qu.:1.0000   1st Qu.:1.0000   1st Qu.:1.0000  
##  Median : 0.0000001   Median :1.0000   Median :1.0000   Median :1.0000  
##  Mean   : 0.2914215   Mean   :0.9946   Mean   :0.9946   Mean   :0.9946  
##  3rd Qu.: 0.7119398   3rd Qu.:1.0000   3rd Qu.:1.0000   3rd Qu.:1.0000  
##  Max.   : 0.9807853   Max.   :2.0000   Max.   :2.0000   Max.   :2.0000  
##  match_accuracy_result    status         
##  Min.   : 0.00000      Length:49244      
##  1st Qu.: 0.00000      Class :character  
##  Median : 0.00000      Mode  :character  
##  Mean   : 0.03784                        
##  3rd Qu.: 0.00000                        
##  Max.   :22.31002
demographic$Timestamp <- trimws(demographic$Timestamp)
demographic$Timestamp <- dmy_hms(demographic$Timestamp, tz = "Australia/Melbourne")
## Warning: All formats failed to parse. No formats found.
str(demographic)
## spc_tbl_ [12 × 8] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ UserID               : num [1:12] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Timestamp            : POSIXct[1:12], format: NA NA ...
##  $ Academic_level       : chr [1:12] "Graduate Student" "Graduate Student" "Graduate Student" "Graduate Student" ...
##  $ Gender               : chr [1:12] "Man" "Man" "Man" "Man" ...
##  $ Age_group            : chr [1:12] "30-40" "20-30" "30-40" "20-30" ...
##  $ Exp_ARVR             : chr [1:12] "I have no experience" "Familiar (5-20 hours experience)" "I have no experience" "Beginner (less than 5 hours experience)" ...
##  $ Globe_usage_frequency: chr [1:12] "Once every few years" "Once every few years" "Once every few years" "A few times a month" ...
##  $ Have_used_VisionPro  : chr [1:12] "I have never used the Apple Vision Pro" "I have never used the Apple Vision Pro" "I have never used the Apple Vision Pro" "I have never used the Apple Vision Pro" ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   UserID = col_double(),
##   ..   Timestamp = col_character(),
##   ..   Academic_level = col_character(),
##   ..   Gender = col_character(),
##   ..   Age_group = col_character(),
##   ..   Exp_ARVR = col_character(),
##   ..   Globe_usage_frequency = col_character(),
##   ..   Have_used_VisionPro = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>
summary(demographic)
##      UserID        Timestamp   Academic_level        Gender         
##  Min.   : 1.00   Min.   :NA    Length:12          Length:12         
##  1st Qu.: 3.75   1st Qu.:NA    Class :character   Class :character  
##  Median : 6.50   Median :NA    Mode  :character   Mode  :character  
##  Mean   : 6.50   Mean   :NaN                                        
##  3rd Qu.: 9.25   3rd Qu.:NA                                         
##  Max.   :12.00   Max.   :NA                                         
##                  NA's   :12                                         
##   Age_group           Exp_ARVR         Globe_usage_frequency
##  Length:12          Length:12          Length:12            
##  Class :character   Class :character   Class :character     
##  Mode  :character   Mode  :character   Mode  :character     
##                                                             
##                                                             
##                                                             
##                                                             
##  Have_used_VisionPro
##  Length:12          
##  Class :character   
##  Mode  :character   
##                     
##                     
##                     
## 
str(positioning_NRG)
## spc_tbl_ [12 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ UserID              : num [1:12] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Timestamp           : chr [1:12] "23/4/2025 15:38" "23/4/2025 16:47" "24/4/2025 11:24" "25/4/2025 11:34" ...
##  $ Mentally_demanding  : chr [1:12] "1. Very, very low mental effort" "2. Very low mental effort" "4. Rather low mental effort" "1. Very, very low mental effort" ...
##  $ Physically_demanding: chr [1:12] "0. No exertion" "2. Light" "3. Moderate" "0. No exertion" ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   UserID = col_double(),
##   ..   Timestamp = col_character(),
##   ..   Mentally_demanding = col_character(),
##   ..   Physically_demanding = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>
summary(positioning_NRG)
##      UserID       Timestamp         Mentally_demanding Physically_demanding
##  Min.   : 1.00   Length:12          Length:12          Length:12           
##  1st Qu.: 3.75   Class :character   Class :character   Class :character    
##  Median : 6.50   Mode  :character   Mode  :character   Mode  :character    
##  Mean   : 6.50                                                             
##  3rd Qu.: 9.25                                                             
##  Max.   :12.00
str(positioning_RG)
## spc_tbl_ [12 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ UserID              : num [1:12] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Timestamp           : chr [1:12] "23/4/2025 15:33" "23/4/2025 16:49" "24/4/2025 11:20" "25/4/2025 11:37" ...
##  $ Mentally_demanding  : chr [1:12] "3. Low mental effort" "1. Very, very low mental effort" "5. Neither low nor high mental effort" "1. Very, very low mental effort" ...
##  $ Physically_demanding: chr [1:12] "2. Light" "1. Very light" "5. Difficult" "0. No exertion" ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   UserID = col_double(),
##   ..   Timestamp = col_character(),
##   ..   Mentally_demanding = col_character(),
##   ..   Physically_demanding = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>
summary(positioning_RG)
##      UserID       Timestamp         Mentally_demanding Physically_demanding
##  Min.   : 1.00   Length:12          Length:12          Length:12           
##  1st Qu.: 3.75   Class :character   Class :character   Class :character    
##  Median : 6.50   Mode  :character   Mode  :character   Mode  :character    
##  Mean   : 6.50                                                             
##  3rd Qu.: 9.25                                                             
##  Max.   :12.00
str(positioning_preference)
## spc_tbl_ [12 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ UserID                : num [1:12] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Timestamp             : chr [1:12] "23/4/2025 15:50" "23/4/2025 16:56" "24/4/2025 11:27" "25/4/2025 11:42" ...
##  $ Positioning_preference: chr [1:12] "Static orientation: The globe's orientation remains fixed while it moves." "Adaptive orientation: The globe rotates as it moves, so I always see the same side of the Earth." "Static orientation: The globe's orientation remains fixed while it moves." "Static orientation: The globe's orientation remains fixed while it moves." ...
##  $ Positioning_feedback  : chr [1:12] "I prefer the static orientation as it makes me feel more enjoyable and easy to move it.  However, in relation t"| __truncated__ "Static orientation give me a little bit of nausea. And regarding the control, the x and y axis gesture is easy "| __truncated__ "Static is more intuitove because it only display 1 type of direction to control than adaptive. In order to rota"| __truncated__ "I like it when it static it is more dynamic and realistic like a globe should be, to move the globe I think it "| __truncated__ ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   UserID = col_double(),
##   ..   Timestamp = col_character(),
##   ..   Positioning_preference = col_character(),
##   ..   Positioning_feedback = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>
summary(positioning_preference)
##      UserID       Timestamp         Positioning_preference Positioning_feedback
##  Min.   : 1.00   Length:12          Length:12              Length:12           
##  1st Qu.: 3.75   Class :character   Class :character       Class :character    
##  Median : 6.50   Mode  :character   Mode  :character       Mode  :character    
##  Mean   : 6.50                                                                 
##  3rd Qu.: 9.25                                                                 
##  Max.   :12.00
str(rotation_OH)
## spc_tbl_ [12 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ UserID              : num [1:12] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Timestamp           : chr [1:12] "23/4/2025 16:00" "23/4/2025 17:01" "24/4/2025 11:45" "25/4/2025 11:46" ...
##  $ Mentally_demanding  : chr [1:12] "2. Very low mental effort" "6. Rather high mental effort" "4. Rather low mental effort" "1. Very, very low mental effort" ...
##  $ Physically_demanding: chr [1:12] "1. Very light" "0. No exertion" "3. Moderate" "0. No exertion" ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   UserID = col_double(),
##   ..   Timestamp = col_character(),
##   ..   Mentally_demanding = col_character(),
##   ..   Physically_demanding = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>
summary(rotation_OH)
##      UserID       Timestamp         Mentally_demanding Physically_demanding
##  Min.   : 1.00   Length:12          Length:12          Length:12           
##  1st Qu.: 3.75   Class :character   Class :character   Class :character    
##  Median : 6.50   Mode  :character   Mode  :character   Mode  :character    
##  Mean   : 6.50                                                             
##  3rd Qu.: 9.25                                                             
##  Max.   :12.00
str(rotation_TH)
## spc_tbl_ [12 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ UserID              : num [1:12] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Timestamp           : chr [1:12] "23/4/2025 15:57" "23/4/2025 17:04" "24/4/2025 11:35" "25/4/2025 11:53" ...
##  $ Mentally_demanding  : chr [1:12] "5. Neither low nor high mental effort" "3. Low mental effort" "6. Rather high mental effort" "1. Very, very low mental effort" ...
##  $ Physically_demanding: chr [1:12] "2. Light" "0. No exertion" "4. Somewhat difficult" "0.5. Noticable" ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   UserID = col_double(),
##   ..   Timestamp = col_character(),
##   ..   Mentally_demanding = col_character(),
##   ..   Physically_demanding = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>
summary(rotation_TH)
##      UserID       Timestamp         Mentally_demanding Physically_demanding
##  Min.   : 1.00   Length:12          Length:12          Length:12           
##  1st Qu.: 3.75   Class :character   Class :character   Class :character    
##  Median : 6.50   Mode  :character   Mode  :character   Mode  :character    
##  Mean   : 6.50                                                             
##  3rd Qu.: 9.25                                                             
##  Max.   :12.00
str(rotation_preference)
## spc_tbl_ [12 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ UserID             : num [1:12] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Timestamp          : chr [1:12] "23/4/2025 16:04" "23/4/2025 17:09" "24/4/2025 11:48" "25/4/2025 11:55" ...
##  $ Rotation_preference: chr [1:12] "One-handed rotation gesture" "Two-handed rotation gesture" "One-handed rotation gesture" "One-handed rotation gesture" ...
##  $ Rotation_feedback  : chr [1:12] "I feel more convenient to use one-handed rotation gesture because it is less confusing compared to two-handed r"| __truncated__ "I have more control with the two-handed rotation gesture, it feels more natural. But still feel limited In term"| __truncated__ "More fingers means more calorie burns. But it has limitation with the control, not sure how to solve or give ge"| __truncated__ "I like one handed better because I have more control to rotate the orientations as I like, as for the gestures "| __truncated__ ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   UserID = col_double(),
##   ..   Timestamp = col_character(),
##   ..   Rotation_preference = col_character(),
##   ..   Rotation_feedback = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>
summary(rotation_preference)
##      UserID       Timestamp         Rotation_preference Rotation_feedback 
##  Min.   : 1.00   Length:12          Length:12           Length:12         
##  1st Qu.: 3.75   Class :character   Class :character    Class :character  
##  Median : 6.50   Mode  :character   Mode  :character    Mode  :character  
##  Mean   : 6.50                                                            
##  3rd Qu.: 9.25                                                            
##  Max.   :12.00
str(scale_MG)
## spc_tbl_ [12 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ UserID              : num [1:12] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Timestamp           : chr [1:12] "23/4/2025 16:07" "23/4/2025 17:13" "24/4/2025 11:49" "25/4/2025 11:58" ...
##  $ Mentally_demanding  : chr [1:12] "1. Very, very low mental effort" "1. Very, very low mental effort" "2. Very low mental effort" "1. Very, very low mental effort" ...
##  $ Physically_demanding: chr [1:12] "0.5. Noticable" "0. No exertion" "1. Very light" "0. No exertion" ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   UserID = col_double(),
##   ..   Timestamp = col_character(),
##   ..   Mentally_demanding = col_character(),
##   ..   Physically_demanding = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>
summary(scale_MG)
##      UserID       Timestamp         Mentally_demanding Physically_demanding
##  Min.   : 1.00   Length:12          Length:12          Length:12           
##  1st Qu.: 3.75   Class :character   Class :character   Class :character    
##  Median : 6.50   Mode  :character   Mode  :character   Mode  :character    
##  Mean   : 6.50                                                             
##  3rd Qu.: 9.25                                                             
##  Max.   :12.00
str(scale_NMG)
## spc_tbl_ [12 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ UserID              : num [1:12] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Timestamp           : chr [1:12] "23/4/2025 16:09" "23/4/2025 17:12" "24/4/2025 11:51" "25/4/2025 11:57" ...
##  $ Mentally_demanding  : chr [1:12] "1. Very, very low mental effort" "1. Very, very low mental effort" "2. Very low mental effort" "1. Very, very low mental effort" ...
##  $ Physically_demanding: chr [1:12] "0.5. Noticable" "0. No exertion" "1. Very light" "0. No exertion" ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   UserID = col_double(),
##   ..   Timestamp = col_character(),
##   ..   Mentally_demanding = col_character(),
##   ..   Physically_demanding = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>
summary(scale_NMG)
##      UserID       Timestamp         Mentally_demanding Physically_demanding
##  Min.   : 1.00   Length:12          Length:12          Length:12           
##  1st Qu.: 3.75   Class :character   Class :character   Class :character    
##  Median : 6.50   Mode  :character   Mode  :character   Mode  :character    
##  Mean   : 6.50                                                             
##  3rd Qu.: 9.25                                                             
##  Max.   :12.00
str(scale_preference)
## spc_tbl_ [12 × 4] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ UserID          : num [1:12] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Timestamp       : chr [1:12] "23/4/2025 16:15" "23/4/2025 17:17" "24/4/2025 11:57" "25/4/2025 12:00" ...
##  $ Scale_preference: chr [1:12] "Maintain distance to globe: The position of the globe moves while its size changes." "Maintain distance to globe: The position of the globe moves while its size changes." "Maintain distance to globe: The position of the globe moves while its size changes." "Maintain distance to globe: The position of the globe moves while its size changes." ...
##  $ Scale_feedback  : chr [1:12] "I prefer maintain globe position since it makes me easy to observe the globe closely and clearly, because I thi"| __truncated__ "For me personally I like to use the maintain distance to globe behaviour because its easier to see when observi"| __truncated__ "For the scope of this globe experiment, I prefer “maintain distance…”, because I do not think it is necessary t"| __truncated__ "I like the 2nd options better so we can observe the globe more detail, without being worry about the globe disa"| __truncated__ ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   UserID = col_double(),
##   ..   Timestamp = col_character(),
##   ..   Scale_preference = col_character(),
##   ..   Scale_feedback = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>
summary(scale_preference)
##      UserID       Timestamp         Scale_preference   Scale_feedback    
##  Min.   : 1.00   Length:12          Length:12          Length:12         
##  1st Qu.: 3.75   Class :character   Class :character   Class :character  
##  Median : 6.50   Mode  :character   Mode  :character   Mode  :character  
##  Mean   : 6.50                                                           
##  3rd Qu.: 9.25                                                           
##  Max.   :12.00
str(combined_preference)
## spc_tbl_ [12 × 6] (S3: spec_tbl_df/tbl_df/tbl/data.frame)
##  $ UserID                         : num [1:12] 1 2 3 4 5 6 7 8 9 10 ...
##  $ Timestamp                      : chr [1:12] "23/4/2025 16:18" "23/4/2025 17:22" "24/4/2025 12:01" "25/4/2025 12:04" ...
##  $ Combined_positioning_preference: chr [1:12] "Static orientation: The globe's orientation remains fixed while it moves." "Adaptive orientation: The globe rotates as it moves, so I always see the same side of the Earth." "Static orientation: The globe's orientation remains fixed while it moves." "Adaptive orientation: The globe rotates as it moves, so I always see the same side of the Earth." ...
##  $ Combined_rotation_preference   : chr [1:12] "One-handed rotation behaviour" "Two-handed rotation behaviour" "One-handed rotation behaviour" "One-handed rotation behaviour" ...
##  $ Combined_scale_preference      : chr [1:12] "Maintain globe position: The position of the globe remains unchanged, irrespective of size adjustments" "Maintain distance to globe: The position of the globe moves while its size changes." "Maintain distance to globe: The position of the globe moves while its size changes." "Maintain distance to globe: The position of the globe moves while its size changes." ...
##  $ Combined_feedback              : chr [1:12] "My preference remains the same even though they are combined." "More advance control could be implemented such as adding a feature when using 2 hands simultaneously to move th"| __truncated__ "My preferences remain the same if asked multiple behaviour of combination. I standstill like bamboo" "My feedbacks are still the same like the others sessions, but once we combined all the methods I like the adapt"| __truncated__ ...
##  - attr(*, "spec")=
##   .. cols(
##   ..   UserID = col_double(),
##   ..   Timestamp = col_character(),
##   ..   Combined_positioning_preference = col_character(),
##   ..   Combined_rotation_preference = col_character(),
##   ..   Combined_scale_preference = col_character(),
##   ..   Combined_feedback = col_character()
##   .. )
##  - attr(*, "problems")=<externalptr>
summary(combined_preference)
##      UserID       Timestamp         Combined_positioning_preference
##  Min.   : 1.00   Length:12          Length:12                      
##  1st Qu.: 3.75   Class :character   Class :character               
##  Median : 6.50   Mode  :character   Mode  :character               
##  Mean   : 6.50                                                     
##  3rd Qu.: 9.25                                                     
##  Max.   :12.00                                                     
##  Combined_rotation_preference Combined_scale_preference Combined_feedback 
##  Length:12                    Length:12                 Length:12         
##  Class :character             Class :character          Class :character  
##  Mode  :character             Mode  :character          Mode  :character  
##                                                                           
##                                                                           
## 

Normality Test

set.seed(123)
sample_data <- sample(data$match_accuracy_result, 5000)
shapiro.test(sample_data)
## 
##  Shapiro-Wilk normality test
## 
## data:  sample_data
## W = 0.16751, p-value < 2.2e-16
hist(data$match_accuracy_result, breaks = 100,
     main = "Histogram (Zoomed)", xlab = "Accuracy",
     col = "lightblue", xlim = c(-1, 100))

plot(density(data$match_accuracy_result), 
     main = "Density Plot (Zoomed)", xlab = "Accuracy",
     col = "darkgreen", lwd = 2, xlim = c(-1, 5))

qqnorm(data$match_accuracy_result); qqline(data$match_accuracy_result, col = "red")

log_data <- log(data$match_accuracy_result + 1e-6)
hist(log_data, breaks = 100, main = "Log-transformed", col = "lightgreen")

qqnorm(log_data); qqline(log_data, col = "red")

sqrt_data <- sqrt(data$match_accuracy_result)
hist(sqrt_data, breaks = 100, main = "Sqrt-transformed", col = "lightcoral")

qqnorm(sqrt_data); qqline(sqrt_data, col = "red")

data %>%
  filter(match_accuracy_result >= 2)
## # A tibble: 231 × 35
##    UserID TaskID         ActionID  rotateGlobeWhileDrag…¹ oneHandedRotationGes…²
##     <dbl> <chr>          <chr>     <lgl>                  <lgl>                 
##  1      1 U1_R_THS_0050  5A285236… FALSE                  FALSE                 
##  2      1 U1_R_THS_0050  5A285236… FALSE                  FALSE                 
##  3      1 U1_R_THS_0050  055111A2… FALSE                  FALSE                 
##  4      1 U1_R_THC_0053  81960F65… FALSE                  FALSE                 
##  5      1 U1_R_THC_0053  13EEFCAC… FALSE                  FALSE                 
##  6      1 U1_R_THC_0055  BE6677B0… FALSE                  FALSE                 
##  7      1 U1_R_THC_0055  4965AC4C… FALSE                  FALSE                 
##  8      1 U1_R_OHS_0060  622C20BA… FALSE                  TRUE                  
##  9      1 U1_R_OHC_0061  7C9339BB… FALSE                  TRUE                  
## 10      2 U2_P_RGFH_0029 948A9879… TRUE                   TRUE                  
## # ℹ 221 more rows
## # ℹ abbreviated names: ¹​rotateGlobeWhileDragging, ²​oneHandedRotationGesture
## # ℹ 30 more variables: moveGlobeWhileScaling <lgl>, distance <chr>,
## #   direction <chr>, complexity <chr>, zoomDirection <chr>, Date <dttm>,
## #   Type <chr>, ActionStatus <chr>, main_translation_x <dbl>,
## #   main_translation_y <dbl>, main_translation_z <dbl>, main_rotation_x <dbl>,
## #   main_rotation_y <dbl>, main_rotation_z <dbl>, main_rotation_w <dbl>, …
nrow(data)
## [1] 49244
z_scores <- scale(data$match_accuracy_result)
outliers_z <- which(abs(z_scores) > 3)
length(outliers_z)
## [1] 700
summary(data$match_accuracy_result[outliers_z])
##    Min. 1st Qu.  Median    Mean 3rd Qu.    Max. 
##  0.8366  1.0854  1.5005  1.7609  2.1759 22.3100
Q1 <- quantile(data$match_accuracy_result, 0.25)
Q3 <- quantile(data$match_accuracy_result, 0.75)
IQR_val <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR_val
upper_bound <- Q3 + 1.5 * IQR_val
outliers_iqr <- data$match_accuracy_result < lower_bound | data$match_accuracy_result > upper_bound
sum(outliers_iqr)
## [1] 3261
summary(data$match_accuracy_result[outliers_iqr])
##      Min.   1st Qu.    Median      Mean   3rd Qu.      Max. 
##  0.000573  0.063969  0.299743  0.571416  0.716408 22.310022
boxplot(data$match_accuracy_result, main = "Boxplot of Accuracy", horizontal = TRUE)

Check visually

hist(data$match_accuracy_result)

qqnorm(data$match_accuracy_result)

Total number of participants

length(unique(data$UserID))
## [1] 12

Descriptive Statistics

Accuracy under certain conditions

data %>%
  filter(rotateGlobeWhileDragging == TRUE & Type == "positionTask") %>%
  summarise(mean_accuracy = mean(match_accuracy_result, na.rm = TRUE),
            sd_accuracy   = sd(match_accuracy_result, na.rm = TRUE),
            count         = n())
## # A tibble: 1 × 3
##   mean_accuracy sd_accuracy count
##           <dbl>       <dbl> <int>
## 1        0.0102       0.107 12441
data %>%
  filter(rotateGlobeWhileDragging == FALSE & Type == "positionTask") %>%
  summarise(mean_accuracy = mean(match_accuracy_result, na.rm = TRUE),
            sd_accuracy   = sd(match_accuracy_result, na.rm = TRUE),
            count         = n())
## # A tibble: 1 × 3
##   mean_accuracy sd_accuracy count
##           <dbl>       <dbl> <int>
## 1        0.0131       0.119 11296
data %>%
  filter(oneHandedRotationGesture == TRUE & Type == "rotationTask") %>%
  summarise(mean_accuracy = mean(match_accuracy_result, na.rm = TRUE),
            sd_accuracy   = sd(match_accuracy_result, na.rm = TRUE),
            count         = n())
## # A tibble: 1 × 3
##   mean_accuracy sd_accuracy count
##           <dbl>       <dbl> <int>
## 1        0.0808       0.351  7899
data %>%
  filter(oneHandedRotationGesture == FALSE & Type == "rotationTask") %>%
  summarise(mean_accuracy = mean(match_accuracy_result, na.rm = TRUE),
            sd_accuracy   = sd(match_accuracy_result, na.rm = TRUE),
            count         = n())
## # A tibble: 1 × 3
##   mean_accuracy sd_accuracy count
##           <dbl>       <dbl> <int>
## 1        0.0654       0.308 11933
data %>%
  filter(moveGlobeWhileScaling == TRUE & Type == "scaleTask") %>%
  summarise(mean_accuracy = mean(match_accuracy_result, na.rm = TRUE),
            sd_accuracy   = sd(match_accuracy_result, na.rm = TRUE),
            count         = n())
## # A tibble: 1 × 3
##   mean_accuracy sd_accuracy count
##           <dbl>       <dbl> <int>
## 1        0.0338       0.504  2692
data %>%
  filter(moveGlobeWhileScaling == FALSE & Type == "scaleTask") %>%
  summarise(mean_accuracy = mean(match_accuracy_result, na.rm = TRUE),
            sd_accuracy   = sd(match_accuracy_result, na.rm = TRUE),
            count         = n())
## # A tibble: 1 × 3
##   mean_accuracy sd_accuracy count
##           <dbl>       <dbl> <int>
## 1        0.0264       0.325  2983

Significant Task & Summary Statistics

data %>%
  group_by(Type) %>%
  summarise(mean_accuracy      = mean(match_accuracy_result, na.rm = TRUE),
            median_completion   = median(Date, na.rm = TRUE),
            total_attempts      = n()) %>%
  arrange(desc(mean_accuracy))
## # A tibble: 3 × 4
##   Type         mean_accuracy median_completion   total_attempts
##   <chr>                <dbl> <dttm>                       <int>
## 1 rotationTask        0.0715 2025-04-26 00:52:10          19832
## 2 scaleTask           0.0299 2025-04-26 01:08:28           5675
## 3 positionTask        0.0116 2025-04-26 00:29:32          23737
kruskal.test(match_accuracy_result ~ Type, data = data)
## 
##  Kruskal-Wallis rank sum test
## 
## data:  match_accuracy_result by Type
## Kruskal-Wallis chi-squared = 211.87, df = 2, p-value < 2.2e-16

Participant Characteristics

data %>%
  filter(Type == "positionTask") %>%
  group_by(UserID, Type) %>%
  summarise(
    total_tasks = n_distinct(TaskID),
    avg_accuracy = mean(match_accuracy_result, na.rm = TRUE),
    used_rotating_globe = sum(rotateGlobeWhileDragging, na.rm = TRUE),
    used_non_rotating_globe = sum(!rotateGlobeWhileDragging, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(avg_accuracy))
## # A tibble: 12 × 6
##    UserID Type         total_tasks avg_accuracy used_rotating_globe
##     <dbl> <chr>              <int>        <dbl>               <int>
##  1      6 positionTask          48     0.0240                   990
##  2      1 positionTask          48     0.0207                  1118
##  3     12 positionTask          48     0.0135                  1032
##  4      9 positionTask          48     0.0124                  1733
##  5     11 positionTask          48     0.0117                  1220
##  6      4 positionTask          48     0.0113                   757
##  7      2 positionTask          48     0.0113                   437
##  8      3 positionTask          48     0.00892                 1311
##  9      7 positionTask          48     0.00805                 1553
## 10     10 positionTask          48     0.00617                  983
## 11      5 positionTask          48     0.00391                  638
## 12      8 positionTask          48     0.000478                 669
## # ℹ 1 more variable: used_non_rotating_globe <int>
data %>%
  filter(Type == "rotationTask") %>%
  group_by(UserID, Type) %>%
  summarise(
    total_tasks = n_distinct(TaskID),
    avg_accuracy = mean(match_accuracy_result, na.rm = TRUE),
    used_one_hand = sum(oneHandedRotationGesture, na.rm = TRUE),
    used_two_hand = sum(!oneHandedRotationGesture, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(avg_accuracy))
## # A tibble: 12 × 6
##    UserID Type         total_tasks avg_accuracy used_one_hand used_two_hand
##     <dbl> <chr>              <int>        <dbl>         <int>         <int>
##  1      3 rotationTask          16       0.131            689           808
##  2      4 rotationTask          16       0.107            483          1014
##  3      5 rotationTask          16       0.0961           827           982
##  4      6 rotationTask          16       0.0930           483          1092
##  5     10 rotationTask          16       0.0802          1286           728
##  6      1 rotationTask          16       0.0686           441           630
##  7      8 rotationTask          16       0.0673           628          1114
##  8      9 rotationTask          16       0.0561           328           922
##  9     11 rotationTask          16       0.0551           479          1095
## 10     12 rotationTask          16       0.0462           571           609
## 11      2 rotationTask          16       0.0417           797           362
## 12      7 rotationTask          16       0.0374           887          2577
data %>%
  filter(Type == "scaleTask") %>%
  group_by(UserID, Type) %>%
  summarise(
    total_tasks = n_distinct(TaskID),
    avg_accuracy = mean(match_accuracy_result, na.rm = TRUE),
    used_moving_globe = sum(moveGlobeWhileScaling, na.rm = TRUE),
    used_non_moving_globe = sum(!moveGlobeWhileScaling, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  arrange(desc(avg_accuracy))
## # A tibble: 12 × 6
##    UserID Type  total_tasks avg_accuracy used_moving_globe used_non_moving_globe
##     <dbl> <chr>       <int>        <dbl>             <int>                 <int>
##  1     11 scal…          16      0.140                 199                   204
##  2      4 scal…          16      0.0594                139                   161
##  3      3 scal…          16      0.0415                180                   242
##  4     10 scal…          16      0.0294                185                   306
##  5     12 scal…          16      0.0251                353                   352
##  6      6 scal…          16      0.0216                215                   358
##  7      5 scal…          16      0.0188                193                   284
##  8      9 scal…          16      0.0182                133                   153
##  9      1 scal…          16      0.0122                218                   228
## 10      7 scal…          16      0.0102                198                   136
## 11      2 scal…          16      0.00980               227                   329
## 12      8 scal…          16      0.00718               452                   230

Visualisation of completion time by number of attempts

## Attempt Distribution
## Bar chart - by Type
data %>%
  group_by(UserID, Type) %>%
  summarise(observations = n(), .groups = "drop") %>%
  ggplot(aes(x = Type, y = observations, fill = Type)) +
  geom_col(position = "dodge") +
  facet_wrap(~UserID, scales = "free_y") +
  labs(title = "Number of Attempts per Task Type by User",
       x = "User ID", y = "Number of Attempts", fill = "Task Type") +
  theme_minimal() +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        strip.text = element_text(size = 8),
        plot.title = element_text(size = 14, face = "bold"))

## Bar chart - by User
data %>%
  group_by(UserID, Type) %>%
  summarise(observations = n(), .groups = "drop") %>%
  ggplot(aes(x = factor(UserID), y = observations, fill = Type)) +
  geom_col(position = "dodge") +
  facet_wrap(~Type, scales = "free_y") +
  labs(title = "Number of Attempts per Task Type by User",
       x = "User ID", y = "Number of Attempts") +
  theme_minimal() +
  theme(
    legend.position = "none",
    axis.text.x = element_text(angle = 45, hjust = 1, size = 8),
    strip.text = element_text(size = 10, face = "bold"),
    plot.title = element_text(size = 14, face = "bold")
  )

## Average Attempts per Task by User
data %>%
  group_by(UserID, Type, TaskID) %>%
  summarise(attempts = n(), .groups = "drop") %>%
  group_by(UserID, Type) %>%
  summarise(avg_attempts = mean(attempts), .groups = "drop") %>%
  ggplot(aes(x = Type, y = avg_attempts, fill = Type)) +
  geom_col(position = "dodge") +
  facet_wrap(~UserID, scales = "free_y") +
  labs(title = "Average Attempts per Task by User",
       x = NULL, y = "Avg Attempts per Task", fill = "Task Type") +
  theme_minimal() +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        strip.text = element_text(size = 8),
        plot.title = element_text(size = 14, face = "bold"))

## Average Attempts per Task all users
data %>%
  group_by(UserID, Type, TaskID) %>%
  summarise(attempts = n(), .groups = "drop") %>%
  group_by(UserID, Type) %>%
  summarise(avg_attempts = mean(attempts), .groups = "drop") %>%
  ggplot(aes(x = Type, y = avg_attempts, fill = Type)) +
  geom_col(position = "dodge") +
  labs(title = "Average Attempts per Task all users",
       x = NULL, y = "Avg Attempts per Task", fill = "Task Type") +
  theme_minimal() +
  theme(axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        strip.text = element_text(size = 8),
        plot.title = element_text(size = 14, face = "bold"))

## Average Attempts per User by Task
data %>%
  group_by(UserID, Type, TaskID) %>%
  summarise(attempts = n(), .groups = "drop") %>%
  group_by(UserID, Type) %>%
  summarise(avg_attempts = mean(attempts), .groups = "drop") %>%
  ggplot(aes(x = UserID, y = avg_attempts, fill = Type)) +
  geom_col(position = "dodge") +
  facet_wrap(~Type, scales = "free_y") +
  labs(title = "Average Attempts per User by Task",
       x = NULL, y = "Avg Attempts per User", fill = "User") +
  theme_minimal() +
  theme(legend.position = "none",
        axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        strip.text = element_text(size = 8),
        plot.title = element_text(size = 14, face = "bold"))

## Average Attempts per User by Task
data %>%
  group_by(UserID, Type, TaskID) %>%
  summarise(attempts = n(), .groups = "drop") %>%
  group_by(UserID, Type) %>%
  summarise(avg_attempts = mean(attempts), .groups = "drop") %>%
  ggplot(aes(x = UserID, y = avg_attempts, fill = Type)) +
  geom_col(position = "dodge") +
  facet_wrap(~Type, scales = "free_y") +
  labs(title = "Average Attempts per User by Task",
       x = NULL, y = "Avg Attempts per User", fill = "User") +
  theme_minimal() +
  theme(legend.position = "none",
        axis.text.x = element_blank(),
        axis.ticks.x = element_blank(),
        strip.text = element_text(size = 8),
        plot.title = element_text(size = 14, face = "bold"))

# Completion time by time taken

# Completion time distribution
# Convert ISO timestamp to POSIXct
data_posixct <- data %>%
  mutate(DateTime = ymd_hms(Date))

# Average Tasks Completion Time
data %>%
  group_by(Type, TaskID) %>%
  summarise(
    completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
    .groups = "drop"
  ) %>%
  group_by(Type) %>%
  summarise(
    avg_completion_time = mean(completion_time, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = Type, y = avg_completion_time, fill = Type)) +
  geom_col(position = "dodge") +
  labs(
    title = "Average Completion Time per User by Task",
    x = NULL, y = "Avg Completion Time (mins)", fill = "User"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    strip.text = element_text(size = 8),
    plot.title = element_text(size = 14, face = "bold")
  )

# Average Total Type Completion Time 
data %>%
  group_by(Type, TaskID) %>%
  summarise(
    completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
    .groups = "drop"
  ) %>%
  group_by(Type) %>%
  summarise(
    avg_completion_time = mean(completion_time, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = Type, y = avg_completion_time, fill = Type)) +
  geom_col(position = "dodge") +
  labs(
    title = "Average Completion Time per User by Task",
    x = NULL, y = "Avg Completion Time (mins)", fill = "User"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    strip.text = element_text(size = 8),
    plot.title = element_text(size = 14, face = "bold")
  )

# Average Total Type Completion Time by Users
data %>%
  group_by(UserID, Type, TaskID) %>%
  summarise(
    completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
    .groups = "drop"
  ) %>%
  group_by(UserID, Type) %>%
  summarise(
    avg_completion_time = mean(completion_time, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = UserID, y = avg_completion_time, fill = Type)) +
  geom_col(position = "dodge") +
  facet_wrap(~Type, scales = "free_y") +
  labs(
    title = "Average Completion Time per User by Task",
    x = NULL, y = "Avg Completion Time (mins)", fill = "User"
  ) +
  theme_minimal() +
  theme(
    legend.position = "none",
    axis.text.x = element_blank(),
    axis.ticks.x = element_blank(),
    strip.text = element_text(size = 8),
    plot.title = element_text(size = 14, face = "bold")
  )

## Average Tasks Completion Time by Users
data %>%
  group_by(UserID, Type, TaskID) %>%
  summarise(
    completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = Type, y = completion_time, fill = Type)) +
  geom_col(position = "dodge") +
  facet_wrap(~UserID, scales = "free_y") +
  labs(
    title = "Total Completion Time per Task Type by User",
    x = "Task Type", y = "Total Time (mins)", fill = "Task Type"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1, size = 8),
    axis.ticks.x = element_blank(),
    strip.text = element_text(size = 8),
    plot.title = element_text(size = 14, face = "bold")
  )

## Average Accuracy per Tasks by users
data %>%
  group_by(UserID, Type) %>%
  summarise(
    avg_accuracy = mean(match_accuracy_result, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = Type, y = avg_accuracy, fill = Type)) +
  geom_col(position = "dodge") +
  facet_wrap(~UserID, scales = "free_y") +
  labs(
    title = "Average Match Accuracy per Task Type by User",
    x = "Task Type", y = "Average Match Accuracy", fill = "Task Type"
  ) +
  theme_minimal() +
  theme(
    axis.text.x = element_text(angle = 45, hjust = 1, size = 8),
    axis.ticks.x = element_blank(),
    strip.text = element_text(size = 8),
    plot.title = element_text(size = 14, face = "bold")
  )

# Accuracy distribution
ggplot(data, aes(x = match_accuracy_result)) +
  geom_density(fill = "skyblue", alpha = 0.6) +
  geom_rug(alpha = 0.1) +
  facet_wrap(~Type, scales = "free_y") +
  coord_cartesian(xlim = c(-0.05, 0.05)) +  
  scale_x_continuous(limits = c(-1, 1)) +
  labs(title = "Distribution of Match Accuracy by Task Type",
       x = "Match Accuracy",
       y = "Density") +
  theme_minimal() +
  theme(strip.text = element_text(size = 10),
        plot.title = element_text(face = "bold"))
## Warning: Removed 593 rows containing non-finite outside the scale range
## (`stat_density()`).

Correlation with factors

# Completion time by gender
data %>%
  group_by(UserID, TaskID) %>%
  summarise(
    completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
    .groups = "drop"
  ) %>%
  inner_join(demographic, by = "UserID") %>%
  group_by(Gender) %>%
  summarise(
    mean_completion_time = mean(completion_time, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = as.factor(Gender), y = mean_completion_time, fill = as.factor(Gender))) +
  geom_col(position = "dodge") +
  labs(
    title = "Average Task Completion Time by Gender",
    x = "Gender",
    y = "Mean Completion Time (mins)"
  ) +
  theme_minimal()

# Accuracy by gender
data %>%
  inner_join(demographic, by = "UserID") %>%
  group_by(Gender, Type) %>%
  summarise(
    mean_accuracy = mean(match_accuracy_result, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = as.factor(Gender), y = mean_accuracy, fill = as.factor(Gender))) +
  geom_col(position = "dodge") +
  facet_wrap(~Type, scales = "free_y") +
  labs(
    title = "Average Match Accuracy by Gender and Type",
    x = "Gender",
    y = "Mean Accuracy",
    fill = "Gender"
  ) +
  theme_minimal()

# Completion time by academic level
data %>%
  group_by(UserID, TaskID) %>%
  summarise(
    completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
    .groups = "drop"
  ) %>%
  inner_join(demographic, by = "UserID") %>%
  group_by(Academic_level) %>%
  summarise(
    mean_completion_time = mean(completion_time, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = as.factor(Academic_level), y = mean_completion_time, fill = as.factor(Academic_level))) +
  geom_col(position = "dodge") +
  labs(
    title = "Average Task Completion Time by Gender",
    x = "Gender",
    y = "Mean Completion Time (mins)"
  ) +
  theme_minimal()

# Accuracy by age academic level
data %>%
  inner_join(demographic, by = "UserID") %>%
  group_by(Academic_level, Type) %>%
  summarise(
    mean_accuracy = mean(match_accuracy_result, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = as.factor(Academic_level), y = mean_accuracy, fill = as.factor(Academic_level))) +
  geom_col(position = "dodge") +
  facet_wrap(~Type, scales = "free_y") +
  labs(
    title = "Average Match Accuracy by Gender and Type",
    x = "Gender",
    y = "Mean Accuracy",
    fill = "Gender"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# Completion time by age group
data %>%
  group_by(UserID, TaskID) %>%
  summarise(
    completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
    .groups = "drop"
  ) %>%
  inner_join(demographic, by = "UserID") %>%
  group_by(Age_group) %>%
  summarise(
    mean_completion_time = mean(completion_time, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = as.factor(Age_group), y = mean_completion_time, fill = as.factor(Age_group))) +
  geom_col(position = "dodge") +
  labs(
    title = "Average Task Completion Time by Gender",
    x = "Gender",
    y = "Mean Completion Time (mins)"
  ) +
  theme_minimal()

# Accuracy by age group
data %>%
  inner_join(demographic, by = "UserID") %>%
  group_by(Age_group, Type) %>%
  summarise(
    mean_accuracy = mean(match_accuracy_result, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = as.factor(Age_group), y = mean_accuracy, fill = as.factor(Age_group))) +
  geom_col(position = "dodge") +
  facet_wrap(~Type, scales = "free_y") +
  labs(
    title = "Average Match Accuracy by Gender and Type",
    x = "Gender",
    y = "Mean Accuracy",
    fill = "Gender"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# Completion time by previous AR/VR experience
data %>%
  group_by(UserID, TaskID) %>%
  summarise(
    completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
    .groups = "drop"
  ) %>%
  inner_join(demographic, by = "UserID") %>%
  group_by(Exp_ARVR) %>%
  summarise(
    mean_completion_time = mean(completion_time, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = as.factor(Exp_ARVR), y = mean_completion_time, fill = as.factor(Exp_ARVR))) +
  geom_col(position = "dodge") +
  labs(
    title = "Average Task Completion Time by Gender",
    x = "Gender",
    y = "Mean Completion Time (mins)"
  ) +
  theme_minimal()

# Accuracy by previous AR/VR experience
data %>%
  inner_join(demographic, by = "UserID") %>%
  group_by(Exp_ARVR, Type) %>%
  summarise(
    mean_accuracy = mean(match_accuracy_result, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = as.factor(Exp_ARVR), y = mean_accuracy, fill = as.factor(Exp_ARVR))) +
  geom_col(position = "dodge") +
  facet_wrap(~Type, scales = "free_y") +
  labs(
    title = "Average Match Accuracy by Gender and Type",
    x = "Gender",
    y = "Mean Accuracy",
    fill = "Gender"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# Completion time by previous globes Experience
data %>%
  group_by(UserID, TaskID) %>%
  summarise(
    completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
    .groups = "drop"
  ) %>%
  inner_join(demographic, by = "UserID") %>%
  group_by(Globe_usage_frequency) %>%
  summarise(
    mean_completion_time = mean(completion_time, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = as.factor(Globe_usage_frequency), y = mean_completion_time, fill = as.factor(Globe_usage_frequency))) +
  geom_col(position = "dodge") +
  labs(
    title = "Average Task Completion Time by Gender",
    x = "Gender",
    y = "Mean Completion Time (mins)"
  ) +
  theme_minimal()

# Accuracy by previous globes Experience
data %>%
  inner_join(demographic, by = "UserID") %>%
  group_by(Globe_usage_frequency, Type) %>%
  summarise(
    mean_accuracy = mean(match_accuracy_result, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = as.factor(Globe_usage_frequency), y = mean_accuracy, fill = as.factor(Globe_usage_frequency))) +
  geom_col(position = "dodge") +
  facet_wrap(~Type, scales = "free_y") +
  labs(
    title = "Average Match Accuracy by Gender and Type",
    x = "Gender",
    y = "Mean Accuracy",
    fill = "Gender"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

# Completion time by previous Apple Vision Pro Experience
data %>%
  group_by(UserID, TaskID) %>%
  summarise(
    completion_time = as.numeric(difftime(max(Date), min(Date), units = "mins")),
    .groups = "drop"
  ) %>%
  inner_join(demographic, by = "UserID") %>%
  group_by(Have_used_VisionPro) %>%
  summarise(
    mean_completion_time = mean(completion_time, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = as.factor(Have_used_VisionPro), y = mean_completion_time, fill = as.factor(Have_used_VisionPro))) +
  geom_col(position = "dodge") +
  labs(
    title = "Average Task Completion Time by Gender",
    x = "Gender",
    y = "Mean Completion Time (mins)"
  ) +
  theme_minimal()

# Accuracy by previous Apple Vision Pro Experience
data %>%
  inner_join(demographic, by = "UserID") %>%
  group_by(Have_used_VisionPro, Type) %>%
  summarise(
    mean_accuracy = mean(match_accuracy_result, na.rm = TRUE),
    .groups = "drop"
  ) %>%
  ggplot(aes(x = as.factor(Have_used_VisionPro), y = mean_accuracy, fill = as.factor(Have_used_VisionPro))) +
  geom_col(position = "dodge") +
  facet_wrap(~Type, scales = "free_y") +
  labs(
    title = "Average Match Accuracy by Gender and Type",
    x = "Gender",
    y = "Mean Accuracy",
    fill = "Gender"
  ) +
  theme_minimal() +
  theme(axis.text.x = element_text(angle = 45, hjust = 1))

Participants preference

library(scales)  
## 
## Attaching package: 'scales'
## The following object is masked from 'package:purrr':
## 
##     discard
## The following object is masked from 'package:readr':
## 
##     col_factor
# Positioning behaviour preference
# positioning_preference %>%
#   mutate(
#     ShortLabel = recode(Positioning_preference,
#       "Static orientation: The globe's orientation remains fixed while it moves." = "Static Orientation",
#       "Adaptive orientation: The globe rotates as it moves, so I always see the same side of the Earth." = "Adaptive Orientation",
#       "I have no preference" = "No Preference"
#     )
#   ) %>%
#   count(ShortLabel) %>%
#   mutate(
#     percent = n / sum(n),
#     ncount = paste0(n, "\n", percent_format()(percent))
#   ) %>%
#   ggplot(aes(x = ShortLabel, y = n, fill = ShortLabel)) +
#   geom_col(color = "white") +  # Use geom_col() for the bar chart
#   geom_text(aes(label = ncount), position = position_stack(vjust = 0.5), size = 4) +  # Labels with count & percentage
#   labs(
#     title = "Distribution of Positioning Preferences",
#     fill = "Preference"
#   ) +
#   theme_minimal() +  # Use a minimal theme for a cleaner look
#   theme(axis.text.x = element_text(angle = 45, hjust = 1))  # Rotate x-axis labels for readability

#Rotating behaviour preference
rotation_preference %>%
  count(Rotation_preference) %>%
  mutate(
    percent = n / sum(n),
    ncount = paste0(n, "\n", percent_format()(percent))
  ) %>%
  ggplot(aes(x = "", y = n, fill = Rotation_preference)) +
  geom_col(width = 1, color = "white") +
  coord_polar(theta = "y") +
  geom_text(aes(label = ncount), position = position_stack(vjust = 0.5), size = 4) +
  labs(
    title = "Distribution of Rotation Preferences",
    fill = "Preference"
  ) +
  theme_void()

#Scaling behaviour preference
# scale_preference %>%
#   mutate(
#     ShortLabel = recode(Scale_preference,
#       "Maintain distance to globe: The position of the globe moves while its size changes." = "Maintain Distance to Globe",
#       "Maintain globe position: The position of the globe remains unchanged, irrespective of size adjustments" = "Maintain Globe Position",
#       "I have no preference" = "No Preference"
#     )
#   ) %>%
#   count(ShortLabel) %>%
#   mutate(
#     percent = n / sum(n),
#     ncount = paste0(n, "\n", percent_format()(percent))
#   ) %>%
#   ggplot(aes(x = "", y = n, fill = ShortLabel)) +
#   geom_col(width = 1, color = "white") +
#   coord_polar(theta = "y") +
#   geom_text(aes(label = ncount), position = position_stack(vjust = 0.5), size = 4) +
#   labs(
#     title = "Distribution of Scale Preferences",
#     fill = "Preference"
#   ) +
#   theme_void()